package com.happy3w.bean.factory;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.happy3w.toolkits.message.MessageRecorderException;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

public class BeanConfigItemResolver implements TypeIdResolver {
    private JavaType baseType;
    private static final Map<String, Class<? extends BeanConfigItem>> typeMap = new HashMap<>();
    public static final Map<JavaType, BeanConfigItemResolver> resolverMap = new HashMap<>();

    public static void registerSubType(Class subType) {
        registerSubType(subType, typeMap, BeanConfigItem.class);
//        for (Map.Entry<JavaType, BeanConfigItemResolver> entry : resolverMap.entrySet()) {
//            if (entry.getKey().getRawClass().isAssignableFrom(subType)) {
//                BeanConfigItemResolver resolver = entry.getValue();
//                Map<String, Class<? extends BeanConfigItem>> targetMap = resolver.typeMap;
//                registerSubType(subType, targetMap, resolver.baseType);
//            }
//        }
    }

    private static void registerSubType(Class subType, Map<String, Class<? extends BeanConfigItem>> targetMap, Class baseType) {
        try {
            Constructor constructor = subType.getConstructor();
            BeanConfigItem item = (BeanConfigItem) constructor.newInstance();
            String type = item.getType();
            if (targetMap.containsKey(type)) {
                throw new MessageRecorderException(
                        MessageFormat.format("Duplicate type:{0}, when init {1} into {2}",
                                type, subType, baseType));
            }
            targetMap.put(type, (Class<? extends BeanConfigItem>) subType);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new MessageRecorderException("Failed to register Sub type:" + subType, e);
        }
    }

    @Override
    public void init(JavaType baseType) {
        this.baseType = baseType;
        resolverMap.put(baseType, this);
    }

    @Override
    public String idFromValue(Object value) {
        if (value instanceof BeanConfigItem) {
            return ((BeanConfigItem) value).getType();
        }
        throw new IllegalArgumentException("Unsupported value: " + value);
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> suggestedType) {
        return idFromValue(value);
    }

    @Override
    public String idFromBaseType() {
        return "beanConfigItem";
    }

    @Override
    public JavaType typeFromId(DatabindContext context, String id) throws IOException {
        Class<? extends BeanConfigItem> clazz = typeMap.get(id);
        if (clazz == null) {
            throw new MessageRecorderException(MessageFormat.format("Unknown type ID: {0}, supported are {1} ",
                    id, typeMap.keySet()));
        }
        return context.constructType(clazz);
    }

    @Override
    public String getDescForKnownTypeIds() {
        return "Custom TypeIdResolver for BeanConfigItem";
    }

    @Override
    public JsonTypeInfo.Id getMechanism() {
        return JsonTypeInfo.Id.CUSTOM;
    }
}
